/*
 * ClassViewerUI.java
 *
 * Created on March 22, 2006, 10:31 AM
 */

package emr.classfileviewer;

import emr.elements.classfileparser.*;
import emr.elements.classfileparser.code.Code;
import emr.elements.common.Element;
import emr.elements.classfileparser.attributes.*;
import emr.elements.classfileparser.cpinfo.*;


import java.io.*;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.swing.tree.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import java.util.Vector;
import java.util.jar.JarInputStream;
import javax.swing.DefaultListModel;
import javax.swing.ListModel;
import javax.swing.UIManager;
/**
 *
 * @author  Ross
 */
public class ClassViewerUI extends javax.swing.JFrame implements TreeSelectionListener 
{
    
    /*  This is the Class file that is to be analyzed */
    private File file;
    
    /*  This class extracts structure from the class file */
    ClassFiles classFileRoot;
    
    DefaultMutableTreeNode rootNode;

    /* This boolean controls whether the enhanced information display is enabled.
     * When enabled, some Elements are handled differently to obtain more information
     * in the valueChanged handler.
     *   true = enhanced mode, used when viewing ClassFile tree structures.
     *   false = standard mode, used when viewing any Element-based tree structure.
     */
    private boolean viewingMode;
    
    /** Creates new form ClassAnalyzerUI */
    public ClassViewerUI() 
    {
        // default to true: viewing ClassFile structures.
        this( true );
       
        
    }
    
    public ClassViewerUI(boolean mode_)
    {
        viewingMode = mode_;

        fileChooser = new javax.swing.JFileChooser();
        //fileChooser.setCurrentDirectory(new File("/projects/java/classfileviewer/build/classes/emr/classfileviewer"));
        fileChooser.setCurrentDirectory(new File("c:/projects/jvm/classfileviewer/build/classes/samples"));
        
        classFileRoot = new ClassFiles();
        
        initComponents();      
        initTree();
    }
    
    /**
     *  Construct a ClassViewer with viewingMode = false (non-specific display mode)
     *  and displays the given text in the file text box.
     */
    public ClassViewerUI(String fileText_)
    {
        this(false);
        selectedFileText.setText(fileText_);
        selectFileButton.setEnabled(false);
        selectedFileText.setEditable(false);
    }



    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jPanel2 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        infoArea = new javax.swing.JTextArea();
        jPanel3 = new javax.swing.JPanel();
        selectFileButton = new javax.swing.JButton();
        selectedFileText = new javax.swing.JTextField();
        analyzeButton = new javax.swing.JButton();
        fileSizeLabel = new javax.swing.JLabel();
        classStructure = new javax.swing.JPanel();
        jScrollPane4 = new javax.swing.JScrollPane();
        classTree = new javax.swing.JTree();
        jTabbedPane1 = new javax.swing.JTabbedPane();
        jScrollPane3 = new javax.swing.JScrollPane();
        displayArea = new javax.swing.JTextArea();
        jPanel1 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        addResolverButton = new javax.swing.JButton();
        jLabel2 = new javax.swing.JLabel();
        jScrollPane6 = new javax.swing.JScrollPane();
        unresolvedList = new javax.swing.JList();
        jScrollPane7 = new javax.swing.JScrollPane();
        resolveAgainstList = new javax.swing.JList();
        jPanel4 = new javax.swing.JPanel();
        jScrollPane5 = new javax.swing.JScrollPane();
        codeCoverageList = new javax.swing.JList();
        jScrollPane9 = new javax.swing.JScrollPane();
        opcodeFoundList = new javax.swing.JList();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("ClassFile Viewer");

        jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Information", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 1, 12))); // NOI18N

        infoArea.setColumns(20);
        infoArea.setEditable(false);
        infoArea.setRows(5);
        jScrollPane1.setViewportView(infoArea);

        org.jdesktop.layout.GroupLayout jPanel2Layout = new org.jdesktop.layout.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 624, Short.MAX_VALUE)
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 76, Short.MAX_VALUE)
        );

        jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Input File", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 1, 12))); // NOI18N

        selectFileButton.setText("Select File...");
        selectFileButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                getSelectedFile(evt);
            }
        });

        analyzeButton.setText("Analyze");
        analyzeButton.setEnabled(false);
        analyzeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                analyzeActionPerformed(evt);
            }
        });

        fileSizeLabel.setText("File size:");

        org.jdesktop.layout.GroupLayout jPanel3Layout = new org.jdesktop.layout.GroupLayout(jPanel3);
        jPanel3.setLayout(jPanel3Layout);
        jPanel3Layout.setHorizontalGroup(
            jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel3Layout.createSequentialGroup()
                .addContainerGap()
                .add(jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING, false)
                    .add(analyzeButton, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .add(selectFileButton, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(selectedFileText, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 505, Short.MAX_VALUE)
                    .add(fileSizeLabel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 188, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                .addContainerGap())
        );
        jPanel3Layout.setVerticalGroup(
            jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel3Layout.createSequentialGroup()
                .add(jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(selectFileButton)
                    .add(selectedFileText, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                .add(15, 15, 15)
                .add(jPanel3Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(analyzeButton)
                    .add(fileSizeLabel)))
        );

        classStructure.setBorder(javax.swing.BorderFactory.createTitledBorder(null, "Class Structure", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 1, 12))); // NOI18N

        classTree.setAutoscrolls(true);
        jScrollPane4.setViewportView(classTree);

        displayArea.setColumns(20);
        displayArea.setRows(5);
        jScrollPane3.setViewportView(displayArea);

        jTabbedPane1.addTab("Component Info", jScrollPane3);

        jLabel1.setText("Resolve against:");

        addResolverButton.setText("Add...");
        addResolverButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addResolverButtonActionPerformed(evt);
            }
        });

        jLabel2.setText("Unresolved dependencies:");

        unresolvedList.setModel(new DefaultListModel());
        jScrollPane6.setViewportView(unresolvedList);

        resolveAgainstList.setModel(new DefaultListModel());
        jScrollPane7.setViewportView(resolveAgainstList);

        org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(jScrollPane6, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 290, Short.MAX_VALUE)
                    .add(jScrollPane7, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 290, Short.MAX_VALUE)
                    .add(jPanel1Layout.createSequentialGroup()
                        .add(jLabel1)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
                        .add(addResolverButton))
                    .add(jLabel2))
                .addContainerGap())
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(jLabel1)
                    .add(addResolverButton))
                .add(7, 7, 7)
                .add(jScrollPane7, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 83, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
                .add(jLabel2)
                .add(7, 7, 7)
                .add(jScrollPane6, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 133, Short.MAX_VALUE)
                .addContainerGap())
        );

        jTabbedPane1.addTab("Dependencies", jPanel1);

        codeCoverageList.setModel(new DefaultListModel());
        codeCoverageList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
            public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
                codeCoverageListValueChanged(evt);
            }
        });
        jScrollPane5.setViewportView(codeCoverageList);

        opcodeFoundList.setModel(new DefaultListModel());
        jScrollPane9.setViewportView(opcodeFoundList);

        org.jdesktop.layout.GroupLayout jPanel4Layout = new org.jdesktop.layout.GroupLayout(jPanel4);
        jPanel4.setLayout(jPanel4Layout);
        jPanel4Layout.setHorizontalGroup(
            jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel4Layout.createSequentialGroup()
                .addContainerGap()
                .add(jScrollPane5, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 133, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .add(6, 6, 6)
                .add(jScrollPane9, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 151, Short.MAX_VALUE)
                .addContainerGap())
        );
        jPanel4Layout.setVerticalGroup(
            jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel4Layout.createSequentialGroup()
                .addContainerGap()
                .add(jPanel4Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(jScrollPane9, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 278, Short.MAX_VALUE)
                    .add(jScrollPane5, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 278, Short.MAX_VALUE))
                .addContainerGap())
        );

        jTabbedPane1.addTab("Code Coverage", jPanel4);

        org.jdesktop.layout.GroupLayout classStructureLayout = new org.jdesktop.layout.GroupLayout(classStructure);
        classStructure.setLayout(classStructureLayout);
        classStructureLayout.setHorizontalGroup(
            classStructureLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(classStructureLayout.createSequentialGroup()
                .add(jScrollPane4, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 303, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .add(6, 6, 6)
                .add(jTabbedPane1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 315, Short.MAX_VALUE))
        );
        classStructureLayout.setVerticalGroup(
            classStructureLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, jScrollPane4, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 339, Short.MAX_VALUE)
            .add(classStructureLayout.createSequentialGroup()
                .add(jTabbedPane1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 328, Short.MAX_VALUE)
                .addContainerGap())
        );

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
                    .add(org.jdesktop.layout.GroupLayout.LEADING, classStructure, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .add(jPanel2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .add(org.jdesktop.layout.GroupLayout.LEADING, jPanel3, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .add(jPanel3, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(classStructure, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jPanel2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void analyzeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_analyzeActionPerformed

        reset();
        
        readClassFile(file, classFileRoot);
        
        buildTreeFromElement(classFileRoot);

        analyzeDependencies();

        getCodeCoverage(classFileRoot);

}//GEN-LAST:event_analyzeActionPerformed

    private void reset() {
        classFileRoot.clear();

        dependencyAnalyzerReset();
        codeCoverageReset();
    }
    /**
     * 
     * @param sourceFile  .class or .jar file
     * @param root root element to add ClassFile objects
     */
    private void readClassFile(File sourceFile, Element root) {

        if(sourceFile == null)
            return;

        //infoArea.setText("");
        infoArea.append("Reading " + sourceFile.getName() + " ...\n");

        InputStream is = null;

        String fileName = sourceFile.getName();

        try {

            is = new FileInputStream(sourceFile);

            if (fileName.endsWith(".jar"))
            {
                //is = new JarInputStream(is);
                //classFileRoot = new JarFile(); // doesn't work!!
                loadClassFilesFromJar(sourceFile.getAbsolutePath(), root);
            }
            else
            {
                root.add(new ClassFile());
                root.readData(is);
            }

            /* read the class file */
            //classFileRoot.readData(is);

        } catch (Exception e) {
            infoArea.append("Read failure: " + e.getMessage() + "\n");
            System.err.println("Exception: " + e.getMessage());
            return;
        }

        infoArea.append("done.\n");
    }


    /**
     *
     * @param jarName
     * @param root - root node to attach new ClassFile objects.
     */
    private void loadClassFilesFromJar(String jarName, Element root)
    {
        System.out.println("Reading " + jarName + " ...");
        try
        {
            java.util.jar.JarFile jarFile = new java.util.jar.JarFile(jarName);
            Enumeration<java.util.jar.JarEntry> entries = jarFile.entries();

            ClassFile classFile = null;
            while(entries.hasMoreElements())
            {
                java.util.jar.JarEntry entry = entries.nextElement();
                // Process .class files
                if ( entry.getName().endsWith(".class") )
                {
                    //System.out.println("   Loading ClassFile from: " + entry.getName());
                    infoArea.append("   Loading ClassFile from: " + entry.getName() + "\n");
                    InputStream is = jarFile.getInputStream(entry);
                    classFile = new ClassFile();
                    classFile.readData(is);

                    // add the class to the ClassFileTree
                    root.add(classFile);
                }
            }
        }
        catch (IOException e)
        {
            System.out.println("Error building table: " + e.getMessage());
        }


    }


    /* initialize the tree */
    private void initTree()
    {
        rootNode = new DefaultMutableTreeNode();
        TreeModel model = new DefaultTreeModel(rootNode);
        
        classTree.setModel(model);
        classTree.addTreeSelectionListener(this);
        
        classTree.setRootVisible(false);
        

    }
    
    /* public method used to build tree from arbitrary Element tree */
    public void buildTreeFromElement(Element element)
    {
        initTree();
        rootNode.setUserObject(element);
        buildTree(element, rootNode);
        classTree.setRootVisible(true);
         
    }
    
    /* build the tree from the classFile */
    private void buildTree(Element classFileElement, DefaultMutableTreeNode node)
    {
        // recurse through the classFile and add nodes for each object
        //System.out.println("building tree... " +  classFileElement.getValue());
        //System.out.println("building tree... " +  classFileElement.getClass().getName() + classFileElement.getValue());
        // for every sub element...
        for(int i = 0; i < classFileElement.size(); i++)
        {
            Element e = classFileElement.get(i);
            DefaultMutableTreeNode childNode = new DefaultMutableTreeNode();
            childNode.setUserObject(e);
            node.add(childNode);
            buildTree(e, childNode);
        }
        
    }

   /***************************************************
    *
    *  Required by TreeSelectionListener interface
    *
    *  Place code here to tailor the display based on
    *  particular ClassFile Elements
    *
    ***************************************************/
    
    public void valueChanged(TreeSelectionEvent e) 
    {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode)
                           classTree.getLastSelectedPathComponent();

        if (node == null) 
            return;
        
        /* get the selected Element */
        Element element = (Element)node.getUserObject();

        if (element == null)
            return;

        /* Do code coverage analysis on the tree rooted by this element */
        getCodeCoverage(element);
        
        /* get the Element name and vary output accordingly */
        String eName = element.toString();

        /* Display the size as the size of the subtree headed by this element */
        int size = element.getTreeSize(false);

        // These features are common to all elements
        StringBuilder contents = new StringBuilder();
        contents.append("   Value : " + element.getValue()    + "   0x" + Integer.toHexString(element.getValue()) + "\n");
        contents.append("  Offset : " + element.getOffset()   + "   0x" + Integer.toHexString(element.getOffset()) + "\n");
        contents.append("    Size : " + size     + "\n");
        contents.append("writable : " + element.isWritable()  + "\n" );
        
        String metaData = element.getMetaData();
        if( metaData == null )
            metaData = "<empty>";

        contents.append("metadata : " + metaData + "\n");
        
        if( viewingMode == true)
        {
            // This stuff is only applicable to ClassFile structures with standard constant pools
            if(eName.equalsIgnoreCase("name_index") ||
               eName.equalsIgnoreCase("descriptor_index"))
            {
                int index = element.getValue();
                // This won't work now: elements would need reference to their parent or enclosing ClassFile instance.
                //ConstantPool constantPool = (ConstantPool)element.getElement("constant_pool");
                //ConstantUTF8Info info = (ConstantUTF8Info)constantPool.getElement(index).getElement("constant_utf8_info");
                //contents.append(info.getString());
            }
            else if(eName.equalsIgnoreCase("constant_utf8_info"))
            {
                ConstantUTF8Info info = (ConstantUTF8Info)element;
                contents.append(info.getString());
            }
            else  // for all other Elements
            {
            
            }
        }
        
         
        // display the info
        displayArea.setText("");
        displayArea.append(contents.toString());
 
    }    
    
    
    private void getSelectedFile(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_getSelectedFile

        fileChooser.setCurrentDirectory(new File("c:/java/classanalyzer/build/classes/emr/classanalyzer/samples"));
        int returnVal = fileChooser.showOpenDialog(this);

        if (returnVal == javax.swing.JFileChooser.APPROVE_OPTION) {
            file = fileChooser.getSelectedFile();
            String fileName = file.getName();
            

            if(fileName.endsWith(".class") || fileName.endsWith(".jar")) {
                selectedFileText.setText(file.getPath());
                fileSizeLabel.setText("File size: " + file.length() + " bytes");
                analyzeButton.setEnabled(true);
            } else {
                selectedFileText.setText("Invalid File");
                analyzeButton.setEnabled(false);
            }
            
        } else {
        }

    }//GEN-LAST:event_getSelectedFile


    ////////////////////////////////////////////////////////////////////////
    //
    //  Dependency Analyzer module
    //

    ClassFiles resolveRoot = new ClassFiles();

    private void addResolverButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addResolverButtonActionPerformed

        //fileChooser.setCurrentDirectory(new File("c:/java/classanalyzer/build/classes/emr/classanalyzer/samples"));
        int returnVal = fileChooser.showOpenDialog(this);

        if (returnVal == javax.swing.JFileChooser.APPROVE_OPTION) {
            final File f = fileChooser.getSelectedFile();
            String fileName = f.getName();

            if(fileName.endsWith(".class") || fileName.endsWith(".jar")) {
                java.awt.EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        DefaultListModel model = (DefaultListModel)resolveAgainstList.getModel();
                        model.addElement(f.getAbsolutePath());
                    }
                });

                readClassFile(f, resolveRoot);
            }
        }

        // Trigger resolution against the new list of libraries
        analyzeDependencies();
    }//GEN-LAST:event_addResolverButtonActionPerformed

    private void codeCoverageListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_codeCoverageListValueChanged
        // TODO add your handling code here:
        if (evt.getValueIsAdjusting() == false) {
            int index = codeCoverageList.getSelectedIndex();
            if (index != -1) {
                ListModel model = (ListModel)codeCoverageList.getModel();
                String text = (String)model.getElementAt(index);
                Set<String> set = opcodeMap.get(text);
                DefaultListModel dModel = (DefaultListModel)opcodeFoundList.getModel();
                dModel.clear();
                if (set != null) {
                    for (String entry : set) {
                        dModel.addElement(entry);
                    }
                }
            }
        }


    }//GEN-LAST:event_codeCoverageListValueChanged


    private void analyzeDependencies()
    {
        // Procedure:
        //    1. Generate a Set of all ClassInfo targets in all ClassFile's
        //       ConstantPools for the ClassFiles root, and for Resolver root.
        //    2. Perform a Set.removeAll() against the Set of ClassFile names
        //       and the resolvers Set and place results into the unresolved dependency list.
        //
        //
        // For each dependency could also compile a list of called methods and fields for
        // use with a tooltip feature.  To do this we would start the lookup with
        // MethodRefInfo and FieldRefInfo and InterfaceMethodRefInfo in the constant pool.
        // It would be nice to have an alternate view of the constant pool - one that
        // contained maps that linked everything.  The maps would just map the source
        // entries directly to the UTF8Info entries.

        Set<String> dependencies = new TreeSet<String>();
        Set<String> resolvers = new TreeSet<String>();

        // Get Set of all symbols in the ClassFiles heirarchy.
        for (Element e : classFileRoot) {
            ClassFile classFile = (ClassFile)e;

            ConstantPool pool = classFile.getConstantPool();
            Set<String> set = pool.getClassInfoStringSet();
            // Filter out the array types that begin with "["
            for (String s : set) {
                if (!s.startsWith("[")) {
                    dependencies.add(s);
                }
            }
           // dependencies.addAll(set);
        }

        // Resolve against own list of classes first
        resolvers.addAll(classFileRoot.getClassNameSet());

        // Resolve against list of resolvers
        resolvers.addAll(resolveRoot.getClassNameSet());

        Set<String> unresolved = new TreeSet<String>(dependencies);
        unresolved.removeAll(resolvers);


        DefaultListModel model = (DefaultListModel)unresolvedList.getModel();
        model.clear();
        for (String s : unresolved) {
            model.addElement(s);
        }
        
        //if (model.isEmpty()) {
        //    model.addElement("<none>");
        //}

    }

    private void dependencyAnalyzerReset() {
        resolveRoot.clear();
    }

    //
    //   End Dependency Analyzer Module
    //
    ////////////////////////////////////////////////////////////////////////



    ////////////////////////////////////////////////////////////////////////
    //
    //  Code Coverage module
    //

    private void getCodeCoverage(Element root) {

        Set<String> codeSet = computeCodeCoverage(root);

        DefaultListModel model = (DefaultListModel)codeCoverageList.getModel();
        model.clear();
        for (String s : codeSet) {
            model.addElement(s);
        }
    }

    // Map of opcodes to lists of class/methods
    // This is also called a MultiMap (in google collections)
    private Map<String, Set<String>> opcodeMap = new TreeMap<String, Set<String>>();

    private Set<String> computeCodeCoverage(Element root)
    {
        // Compute the bytes codes that are covered starting from the given
        // root element.  This element must be either a:
        //    ClassFiles, ClassFile, Methods, or MethodInfo
        // The idea is that clicks on the tree can be used to narrow down
        // or broaden the scope of coverage as desired.

        // ClassFiles -> ClassFile -> Methods -> MethodInfo[] -> CodeAttribue -> Code
        // Note that native methods won't have a CodeAttribute

        // Cache the elements and any sets that may have been created for them.
        // High level elements that are narrowed in scope result in new
        // sets being created; low level elements that are expanded in scope
        // result in their set being added to the higher level set.  Can't cache
        // any elements until I can identify them uniquely which is difficult.

        // Also build a Map of each byte code to a List of where it was found:
        //   Class + Method
        // This List can be brought up with a "hover over" feature.


        Set<String> codeSet = new TreeSet<String>();

        if (root instanceof ClassFiles) {
            for (Element e : root) {
                ClassFile cf = (ClassFile)e;
                Methods methods = cf.getMethods();
                List<MethodInfo> infoList = methods.getMethods();
                for (MethodInfo info : infoList) {
                    CodeAttribute attr = info.getCodeAttribute();
                    String name = cf.getName() + " " + info.getName() + " " + info.getDescriptor();
                    codeCoverage(attr, codeSet, opcodeMap, name);

                }
            }
        } else if (root instanceof MethodInfo) {
            MethodInfo info = (MethodInfo)root;
            CodeAttribute attr = info.getCodeAttribute();
            String name = info.getName() + " " + info.getDescriptor();
            codeCoverage(attr, codeSet, opcodeMap, name);
        }

        return codeSet;
    }

    /**
     * Parse the opcode from this code attribute and add them to the set.
     * For each opcode, also add to that opcode's map an entry that indicates
     * the class and method this opcode was found in.
     *
     * @param attr
     * @param codeSet - Set to add opcode mnemonics to form the CodeAttribute
     */
    private void codeCoverage(CodeAttribute attr, 
                              Set<String> codeSet,
                              Map<String, Set<String>> opcodeMap,
                              String foundName) {
        if (attr == null)
            return;

        Element code = attr.getCode();

        // Get the instructions' opcodes.  Skip the first code length element.
        for (int i = 1; i < code.size(); i++) {
            Element instruc = code.get(i);
            // All instructions have an opcode element, and their
            // toString() method reports the name.
            //int opcode = instruc.get(0).getValue();
            String opcode = instruc.toString();
            codeSet.add(opcode);
            Set<String> set = opcodeMap.get(opcode);
            if (set == null) {
                set = new TreeSet<String>();
                opcodeMap.put(opcode, set);
            }

            set.add(foundName);
        }
    }

    private void codeCoverageReset() {
        opcodeMap.clear();
    }

    //
    //   End Code Dependency Module
    //
    ////////////////////////////////////////////////////////////////////////

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        // Set the Windows look and feel.
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e)
        {

        }
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new ClassViewerUI().setVisible(true);
            }
        });
    }
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton addResolverButton;
    private javax.swing.JButton analyzeButton;
    private javax.swing.JPanel classStructure;
    private javax.swing.JTree classTree;
    private javax.swing.JList codeCoverageList;
    private javax.swing.JTextArea displayArea;
    private javax.swing.JLabel fileSizeLabel;
    private javax.swing.JTextArea infoArea;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JPanel jPanel4;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JScrollPane jScrollPane4;
    private javax.swing.JScrollPane jScrollPane5;
    private javax.swing.JScrollPane jScrollPane6;
    private javax.swing.JScrollPane jScrollPane7;
    private javax.swing.JScrollPane jScrollPane8;
    private javax.swing.JScrollPane jScrollPane9;
    private javax.swing.JTabbedPane jTabbedPane1;
    private javax.swing.JList opcodeFoundList;
    private javax.swing.JList resolveAgainstList;
    private javax.swing.JButton selectFileButton;
    private javax.swing.JTextField selectedFileText;
    private javax.swing.JList unresolvedList;
    // End of variables declaration//GEN-END:variables
    
    private javax.swing.JFileChooser fileChooser;
    
}
